home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.mactech.com 2010
/
ftp.mactech.com.tar
/
ftp.mactech.com
/
util
/
Leaks-dcmd.sit
/
Leaks dcmd
/
Leaks.a
< prev
next >
Wrap
Text File
|
1991-10-09
|
23KB
|
476 lines
; Leaks.a
;
; Copyright © 1990-91 by Apple Computer, Inc., all rights reserved.
;
; by Bo3b Johnson 10/9/90
; MS 37-DS
;
; This file is the assembly routines I need in order to interface to the toolbox in
; a sensible fashion. The OS is funky, and there is no good way to do these things
; from Pascal or C. The use of INLINE code is OK, but not always enough.
;
; In order to fashion a good patch to NewPtr/NewHandle, I need to use PC
; relative addressing. The basic problem is that there is no good way to get back
; to the dcmd globals, while the patch is executing. Certainly you can't rely upon
; A5, A6, or A7; and no other registers are available for use. I could stick something
; into AppParms, CurApName, or a number of other atrocities, but I don't own
; any of those spots, and could quite easily end up competing with some other code
; for the spot. In the past, this code patched the Chain trap, since it is not in use,
; but that has competition problems too. If I use PC-relative addressing to get to
; some global information, then this becomes self-modifying code, since I will
; change the code in this block, using a piece of code space as variable storage.
; This seems the most risk-free version for now, notwithstanding certain strident
; if misguided warnings from various groups. It's not enough to tell me not to
; do something, you have to give me an alternative. If you have a better alternative,
; by all means let me know.
;
; I patch the four traps of NewPtr/DisposPtr, NewHandle/DisposHandle; and all
; these pieces are almost identical. The only difference really is the variable being
; used. The basic idea is to have one routine to save off the old address, gotten from
; an NGetTrapAddress; and to save that into the code here, using PC-relative addressing.
; The trap is patched by the Pascal code, and whenever it gets called, it will call here
; to have us call through the old version of the trap. That way it gets control before
; the trap executes, but I still drive the old code.
;
; Realize that the code in this file is getting run mostly during the normal operation
; of the Mac. The stack is whatever the current app is using, as well as that A5 world,
; and stack crawl. I don't where I'll be so, I can't rely on those things. This
; is very different from the code in the dcmd, where it knows the stack it is using is
; in Macsbug, and very small. Pieces of this code will get run a tiny bit from the dcmd,
; since it will call here in order to get access to my global variables, the treeTop, emptyQ,
; and activeState.
;
; One of the goals in this file is to avoid needing an asm version of the record structure
; I use. I really just want to pass around pointers, so that this file can be a little
; more independent of the basic structures. One of the things that's get used though is
; the size of the Stack Crawl array that I pass around. There is a constant here, but
; if it changes in the Pascal code, it should change here.
; By the way, this file looks best if viewed in Palatino 12.
include 'SysErr.a'
include 'SysEqu.a'
include 'Traps.a'
proc
import AddNewBlock, KillOldBlock
; ——————————————————————————————————————————————————————————————————————————————————————————————————
;
; Storage for the old patch addresses, used to call through once the patch code executes.
; These are essentially globals, used by the asm code. They are specifically not exported,
; so that the Pascal code cannot access them directly. There are a number of interface
; routines I set up so that Pascal can get and set them, but has to go through this file.
; You know, sort of object like.
;
pOldNewPtr dc.l 0
pOldNewHandle dc.l 0
pOldDisposPtr dc.l 0
pOldDisposHandle dc.l 0
pEmptyQ dc.l 0 ; list of empty elements available.
pTreeTop dc.l 0 ; list of active elements.
pActive dc.w 0 ; whether to watch blocks or not.
saveEm reg a0-a3/d0-d3 ; a few to keep, since I'm a patch.
saveEmSize EQU (8*4) ; 8 registers times 4 bytes each.
kCrawlArraySize EQU 8 ; number of stack crawl elements to do.
; ——————————————————————————————————————————————————————————————————————————————————————————————————
; When I'm am setting up the world, I call NGetTrapAddress to get the old version
; of the traps. I need to save that dude off so I can get back there when needed.
; This routine is a handy interface to the high-level world, isolating this asm junk
; from the code. All these routines are the same, just a different variable being affected.
; This hunk uses the PC-Relative addressing mode in order to get the address of the
; variable being set. This allows the code to function without any explicit global
; space, since the code acts like globals here.
; The interface is:
; PROCEDURE SetOldNewPtr (address: LongInt);
; PROCEDURE SetOldNewHandle (address: LongInt);
; PROCEDURE SetOldDisposPtr (address: LongInt);
; PROCEDURE SetOldDisposHandle (address: LongInt);
export SetOldNewPtr, SetOldNewHandle, SetOldDisposPtr, SetOldDisposHandle
SetOldNewPtr
LEA pOldNewPtr,A1 ; the variable to be setting
BRA.S Common ; do common stuffing code.
SetOldNewHandle
LEA pOldNewHandle,A1 ; the variable to set
BRA.S Common ; do common stuffing code.
SetOldDisposPtr
LEA pOldDisposPtr,A1 ; the variable to do
BRA.S Common ; do common stuffing code.
SetOldDisposHandle
LEA pOldDisposHandle,A1 ; the variable
Common
MOVE.L (SP)+,A0 ; get the return address.
MOVE.L (SP)+,(A1) ; save it, pulling parameter too.
JMP (A0) ; it's saved, return to high-level.
; ——————————————————————————————————————————————————————————————————————————————————————————————————
; These routines are used by the dcmd in order to get to the global variables, to find the
; treeTop, emptyQ, and activeState. They aren't used by the b-tree management stuff,
; since I always pass these variables to those routines. In addition, the patch code above
; doesn't use this code, since it has direct access to the variables, and does so, using the
; pc-relative approach.
;
;
; GetTreeInfo:
; When desired, the pascal callers may need to get the tree info in order to be able to drive
; the trees for info display reasons. This is an interface to let them see us, but I get to
; pass back the info, rather than have them groping around directly for the data.
; FUNCTION GetTreeTop: TrackEntryPtr; EXTERNAL;
; FUNCTION GetEmptyQ: TrackEntryPtr; EXTERNAL;
; FUNCTION TrackActive: Boolean; EXTERNAL;
export GetTreeTop, GetEmptyQ, TrackActive
; GetTreeTop:
; This routine just returns the tree top as a parameter. The assembly stuff just grabs
; these guys directly when it needs to, but the Pascal code comes through this interface
; to get the references.
; FUNCTION GetTreeTop: TreeEntryPtr; EXTERNAL;
GetTreeTop
MOVE.L pTreeTop,4(SP) ; stuff the return result (pc-relative for source.)
RTS ; and return to caller.
; GetEmptyQ:
; FUNCTION GetEmptyQ: TreeEntryPtr; EXTERNAL;
GetEmptyQ
MOVE.L pEmptyQ,4(SP) ; stuff the return result (pc-relative for source.)
RTS ; and return to caller.
; TrackActive:
; Just return the active state as a boolean. This is whether I am actively watching
; and recording block addresses or not. I always want to be able to turn it off sometimes,
; since I want to save a given state for viewing.
; FUNCTION TrackActive: Boolean; EXTERNAL;
TrackActive
MOVE.W pActive,4(SP) ; stuff function result (pc-relative source).
RTS ; and return to caller.
; ——————————————————————————————————————————————————————————————————————————————————————————————————
; As part of the interface to the dcmd, I have setting routines too, to set the variables
; to a known state. This way I don't have to put that sort of tree-based init code in
; assembly. I need to turn it on and off using the dcmd, so it will call here to do so.
;
; PROCEDURE SetTreeTop (address: TrackEntryPtr); EXTERNAL;
; PROCEDURE SetEmptyQ (address: TrackEntryPtr); EXTERNAL;
; PROCEDURE SetActive (state: Boolean); EXTERNAL;
export SetTreeTop, SetEmptyQ, SetActive
; PROCEDURE SetTreeTop (address: TrackEntryPtr); EXTERNAL;
SetTreeTop
MOVE.L (SP)+,A0 ; the return address, for safe keeping.
LEA pTreeTop,A1 ; the variable I need to set, pc-relative.
MOVE.L (SP)+,(A1) ; save off the new variable, clearing parameter.
JMP (A0) ; and return to caller.
; PROCEDURE SetEmptyQ (address: TrackEntryPtr); EXTERNAL;
SetEmptyQ
MOVE.L (SP)+,A0 ; the return address, for safe keeping.
LEA pEmptyQ,A1 ; the variable I need to set, pc-relative.
MOVE.L (SP)+,(A1) ; save off the new variable, clearing parameter.
JMP (A0) ; and return to caller.
; PROCEDURE SetActive (state: Boolean); EXTERNAL;
SetActive
MOVE.L (SP)+,A0 ; the return address, for safe keeping.
LEA pActive,A1 ; the variable I need to set, pc-relative.
MOVE.W (SP)+,(A1) ; save off the new state, clearing parameter.
JMP (A0) ; and return to caller.
; ——————————————————————————————————————————————————————————————————————————————————————————————————
; This is the patch code for the various pieces I patch. This is in assembly
; since I need to preserve registers and do some other low level jacking around,
; like trying to do a stack crawl whenever I allocate blocks. These routines are the
; actual code that gets called when one of the four traps is executed.
; WatchNewPtr:
; The routine to patch the NewPtr trap, and I get first dibs at getting the info I want
; out of the registers. I need to save off the size to start with, since D0 gets pounded
; to the return result once I actually call the old NewPtr code. Once I have that
; safely stowed on the stack, I call the old code, using the PC-relative addressing in
; order to get the old address that I'm supposed to call. This was set up when the
; dcmd called me to save off the value. When that old NewPtr junk returns, I get
; the actual address allocated, if enough memory, then do the funky stack crawl,
; and finally pass all these tidbits off to the AddNewBlock routine.
export WatchNewPtr
WatchNewPtr
MOVE.L D0,-(SP) ; save size off.
; First, allocate the pointer.
PEA @1 ; coming back here, as return address.
MOVE.L pOldNewPtr,-(SP) ; the old address I am going to,
RTS ; JMP there, when it RTSes I come back
@1 ; --here.
; A0 is set up as the handle to track, add it to the b-tree list. If it is NIL, meaning the
; NewPtr failed, then don't add it to the list, it cannot be disposed. I do the save of
; registers here, after the allocation of memory, so that I can save the registers as they
; are coming out of the call, not going in.
MOVEM.L saveEm,-(SP) ; save registers in trap patches.
MOVE.W pActive,D0 ; is it turned on to watch?
BEQ.S @exit ; if not, skip saving.
CMP.L #0,A0 ; is it a NIL return?
BEQ.S @exit ; if so, bag it.
; Now I am supposed to track the block that was just allocated, so I need to call the AddNewBlock
; routine in order to tie it in. In order to set up the parameters for the call, I have to do a stack
; crawl, to get the previous return addresses. The problem of course is that the stack crawl has
; no known length, and is often less than kCrawlArraySize. So... I have to look at each one as
; it is created, and make sure that it is valid; and continue no farther when I find a bad one. If
; I go on, I risk dying from a bus error, since the addresses won't chain nicely in all cases. I will call
; the routine, where the stackToAdd is an array, built on the stack:
; PROCEDURE AddNewBlock (addressToAdd, sizeToAdd: LongInt; stackToAdd: StackArray;
; VAR treeTop, emptyQ: TrackEntryPtr);
MOVE.L saveEmSize(SP),D1 ; retrieve size parameter.
MOVEQ #kCrawlArraySize-1,D0 ; for 8 times in DBRA loop.
@Clr
CLR.L -(SP) ; make a stack crawl array on the stack,
DBRA D0,@Clr ; by looping 8 times. (all zeroed.)
MOVE.L SP,A1 ; the address of the stack crawl array.
; Now with that array built and zeroed I want to add the parameters to the stack. If I have to
; bail out of the stack crawl before it hits all kCrawlArraySize elements, then the rest have
; already been zeroed, which marks them as non-valid. I'm going to set up for the call
; here, so the registers are freed up for doing the funky stack crawl checks.
MOVE.L A0,-(SP) ; addressToAdd: block being added to tracker.
MOVE.L D1,-(SP) ; sizeToAdd: size of block being tracked.
MOVE.L A1,-(SP) ; stackToAdd: the address of the array of crawls.
PEA pTreeTop ; treeTop: bTree top. (as var parameters)
PEA pEmptyQ ; emptyQ: top of empties list.
; All the parameters are set up, do the stack crawl checks. This will modify the stackToAdd
; array, for each crawl that is valid. The stackToAdd is still in A1.
MOVE.L A6,A0 ; get stack frame head.
MOVEQ #kCrawlArraySize-1,D0 ; doing a loop for 8 stack crawls.
@Frame
CMP.L A7,A0 ; make sure it is greater than a7
BLO.S @noFrame ; skip any more if not valid.
CMP.L CurStackBase,A0 ; make sure it is less than base of stack,
BHI.S @noFrame ; If not, skip checking rest of crawl.
; It's a valid frame, go ahead and save off the return address in the array. A1 is the address of
; the next array element to set. It is autoincremented to the next entry each time I can stow a
; valid pc return address.
MOVE.L 4(A0),(A1)+ ; the pc return address for this frame, into Array.
MOVE.L (A0),A0 ; next stack frame back. (as a big chain)
DBRA D0,@Frame ; loop for all 8.
; If there was a bogus frame in there somewhere, I bailed out to here. All the rest of the stackArray
; entries are nil though, so it doesn't matter, I'll just call the AddNewBlock anyway. I got here if
; all the entries were valid, too, and just fell out of the DBRA loop.
@noFrame
BSR AddNewBlock ; track a new block on heap.
ADD.L #kCrawlArraySize*4,SP ; kill the crawl array from stack.
@exit
MOVEM.L (SP)+,saveEm ; save registers in trap patches.
ADDQ #4,SP ; kill size that was saved. (from D0)
RTS ; Return to caller of NewPtr.
; ——————————————————————————————————————————————————————————————————————————————————————————————————
; WatchDisposPtr:
; This routine is my patch to DisposPtr, and it watches the address being passed in
; A0 to see if it is one of the blocks I am tracking in the table. If it is, I release that
; record from my b-Tree code, since it is obviously not a leak when it is released.
; If the block is not found, then I just skip it too, since there are presumably a
; ton of blocks that I didn't see being allocated, since I'm not watching from the
; start of the system. This is a head patch, since I do my thing, then jump to
; the old routine. When that routine finishes, it will call RTS and go back to
; wherever I was called from.
export WatchDisposPtr
WatchDisposPtr
MOVEM.L saveEm,-(SP) ; save registers in trap patches.
MOVE.W pActive,D0 ; is it turned on to watch?
BEQ.S CallOldDisposePtr ; if not, skip saving.
MOVE.L A0,-(SP) ; block being added to tracker.
PEA pTreeTop ; bTree top. (as var parameters)
PEA pEmptyQ ; top of empties list.
BSR KillOldBlock ; get it out of my dang table
CallOldDisposePtr
MOVEM.L (SP)+,saveEm ; save registers in trap patches.
MOVE.L pOldDisposPtr,-(SP) ; get address of old routine.
RTS ; and jump there.
; when it RTSes, I'll go back to the caller.
; ——————————————————————————————————————————————————————————————————————————————————————————————————
; WatchNewHandle:
; The routine to patch the NewHandle trap, and I get first dibs at getting the info I want
; out of the registers. I need to save off the size to start with, since D0 gets pounded
; to the return result once I actually call the old NewHandle code. Once I have that
; safely stowed on the stack, I call the old code, using the PC-relative addressing in
; order to get the old address that I'm supposed to call. This was set up when the
; dcmd called me to save off the value. When that old NewHandle junk returns, I get
; the actual address allocated, if enough memory, then do the funky stack crawl,
; and finally pass all these tidbits off to the AddNewBlock routine. The AddNewBlock
; doesn't differentiate between pointers being watched or handles being watched, it is
; just an address.
export WatchNewHandle
WatchNewHandle
MOVE.L D0,-(SP) ; save size off.
; First, allocate the handle.
PEA @1 ; coming back here, as return address.
MOVE.L pOldNewHandle,-(SP) ; the old address I am going to,
RTS ; JMP there, when it RTSes I come back
@1 ; --here.
; A0 is set up as the handle to track, add it to the b-tree list. If it is NIL, meaning the
; NewHandle failed, then don't add it to the list, it cannot be disposed. I do the save of
; registers here, after the allocation of memory, so that I can save the registers as they
; are coming out of the call, not going in.
MOVEM.L saveEm,-(SP) ; save registers in trap patches.
MOVE.W pActive,D0 ; is it turned on to watch?
BEQ.S @exit ; if not, skip saving.
CMP.L #0,A0 ; is it a NIL return?
BEQ.S @exit ; if so, bag it.
; Now I am supposed to track the block that was just allocated, so I need to call the AddNewBlock
; routine in order to tie it in. In order to set up the parameters for the call, I have to do a stack
; crawl, to get the previous return addresses. The problem of course is that the stack crawl has
; no known length, and is often less than kCrawlArraySize. So... I have to look at each one as
; it is created, and make sure that it is valid; and continue no farther when I find a bad one. If
; I go on, I risk dying from a bus error, since the addresses won't chain nicely in all cases. I will call
; the routine, where the stackToAdd is an array, built on the stack:
; PROCEDURE AddNewBlock (addressToAdd, sizeToAdd: LongInt; stackToAdd: StackArray;
; VAR treeTop, emptyQ: TrackEntryPtr);
MOVE.L saveEmSize(SP),D1 ; retrieve size parameter.
MOVEQ #kCrawlArraySize-1,D0 ; for 8 times in DBRA loop.
@Clr
CLR.L -(SP) ; make a stack crawl array on the stack,
DBRA D0,@Clr ; by looping 8 times. (all zeroed.)
MOVE.L SP,A1 ; the address of the stack crawl array.
; Now with that array built and zeroed I want to add the parameters to the stack. If I have to
; bail out of the stack crawl before it hits all kCrawlArraySize elements, then the rest have
; already been zeroed, which marks them as non-valid. I'm going to set up for the call
; here, so the registers are freed up for doing the funky stack crawl checks.
MOVE.L A0,-(SP) ; addressToAdd: block being added to tracker.
MOVE.L D1,-(SP) ; sizeToAdd: size of block being tracked.
MOVE.L A1,-(SP) ; stackToAdd: the address of the array of crawls.
PEA pTreeTop ; treeTop: bTree top. (as var parameters)
PEA pEmptyQ ; emptyQ: top of empties list.
; All the parameters are set up, do the stack crawl checks. This will modify the stackToAdd
; array, for each crawl that is valid. The stackToAdd is still in A1.
MOVE.L A6,A0 ; get stack frame head.
MOVEQ #kCrawlArraySize-1,D0 ; doing a loop for 8 stack crawls.
@Frame
CMP.L A7,A0 ; make sure it is greater than a7
BLO.S @noFrame ; skip any more if not valid.
CMP.L CurStackBase,A0 ; make sure it is less than base of stack,
BHI.S @noFrame ; If not, skip checking rest of crawl.
; It's a valid frame, go ahead and save off the return address in the array. A1 is the address of
; the next array element to set. It is autoincremented to the next entry each time I can stow a
; valid pc return address.
MOVE.L 4(A0),(A1)+ ; the pc return address for this frame, into Array.
MOVE.L (A0),A0 ; next stack frame back. (as a big chain)
DBRA D0,@Frame ; loop for all 8.
; If there was a bogus frame in there somewhere, I bailed out to here. All the rest of the stackArray
; entries are nil though, so it doesn't matter, I'll just call the AddNewBlock anyway. I got here if
; all the entries were valid, too, and just fell out of the DBRA loop.
@noFrame
BSR AddNewBlock ; track a new block on heap.
ADD.L #kCrawlArraySize*4,SP ; kill the crawl array from stack.
@exit
MOVEM.L (SP)+,saveEm ; save registers in trap patches.
ADDQ #4,SP ; kill size that was saved. (from D0)
RTS ; Return to caller of NewPtr.
; ——————————————————————————————————————————————————————————————————————————————————————————————————
; WatchDisposHandle:
; This routine is my patch to DisposHandle, and it watches the address being passed in
; A0 to see if it is one of the blocks I am tracking in the table. If it is, I release that
; record from my b-Tree code, since it is obviously not a leak when it is released.
; If the block is not found, then I just skip it too, since there are presumably a
; ton of blocks that I didn't see being allocated, since I'm not watching from the
; start of the system. This is a head patch, since I do my thing, then jump to
; the old routine. When that routine finishes, it will call RTS and go back to
; wherever I was called from.
export WatchDisposHandle
WatchDisposHandle
MOVEM.L saveEm,-(SP) ; save registers in trap patches.
MOVE.W pActive,D0 ; is it turned on to watch?
BEQ.S CallOldDisposeHandle ; if not, skip saving.
MOVE.L A0,-(SP) ; block being added to tracker.
PEA pTreeTop ; bTree top. (as var parameters)
PEA pEmptyQ ; top of empties list.
BSR KillOldBlock ; get it out of my dang table
CallOldDisposeHandle
MOVEM.L (SP)+,saveEm ; save registers in trap patches.
MOVE.L pOldDisposHandle,-(SP) ; get address of old routine.
RTS ; and jump there.
; when it RTSes, I'll go back to the caller.
endproc
; ——————————————————————————————————————————————————————————————————————————————————————————————————
end